#define JPEG_INTERNALS
#include "jinclude.h"
#include "jpeglib.h"


/* Pointer to routine to downsample a single component */
typedef JMETHOD( void, downsample1_ptr,
                 ( j_compress_ptr cinfo, jpeg_component_info * compptr,
                   JSAMPARRAY input_data, JSAMPARRAY output_data ) );

/* Private subobject */

typedef struct {
  struct jpeg_downsampler pub;	/* public fields */

  /* Downsampling method pointers, one per component */
  downsample1_ptr methods[MAX_COMPONENTS];
} my_downsampler;

typedef my_downsampler * my_downsample_ptr;


/*
   Initialize for a downsampling pass.
*/

METHODDEF( void )
start_pass_downsample( j_compress_ptr cinfo ) {
  /* no work for now */
}

static void expand_right_edge( JSAMPARRAY image_data, int num_rows,
                               JDIMENSION input_cols, JDIMENSION output_cols ) {
  register JSAMPROW ptr;
  register JSAMPLE pixval;
  register int count;
  int row;
  int numcols = ( int )( output_cols - input_cols );
  if( numcols > 0 ) {
    for( row = 0; row < num_rows; row++ ) {
      ptr = image_data[row] + input_cols;
      pixval = ptr[-1];		/* don't need GETJSAMPLE() here */
      for( count = numcols; count > 0; count-- )
      { *ptr++ = pixval; }
    }
  }
}


/*
   Do downsampling for a whole row group (all components).

   In this version we simply downsample each component independently.
*/

METHODDEF( void )
sep_downsample( j_compress_ptr cinfo,
                JSAMPIMAGE input_buf, JDIMENSION in_row_index,
                JSAMPIMAGE output_buf, JDIMENSION out_row_group_index ) {
  my_downsample_ptr downsample = ( my_downsample_ptr ) cinfo->downsample;
  int ci;
  jpeg_component_info * compptr;
  JSAMPARRAY in_ptr, out_ptr;
  for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
       ci++, compptr++ ) {
    in_ptr = input_buf[ci] + in_row_index;
    out_ptr = output_buf[ci] + ( out_row_group_index * compptr->v_samp_factor );
    ( *downsample->methods[ci] )( cinfo, compptr, in_ptr, out_ptr );
  }
}


/*
   Downsample pixel values of a single component.
   One row group is processed per call.
   This version handles arbitrary integral sampling ratios, without smoothing.
   Note that this version is not actually used for customary sampling ratios.
*/

METHODDEF( void )
int_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr,
                JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  int inrow, outrow, h_expand, v_expand, numpix, numpix2, h, v;
  JDIMENSION outcol, outcol_h;	/* outcol_h == outcol*h_expand */
  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
  JSAMPROW inptr, outptr;
  JPEG_INT32 outvalue;
  h_expand = cinfo->max_h_samp_factor / compptr->h_samp_factor;
  v_expand = cinfo->max_v_samp_factor / compptr->v_samp_factor;
  numpix = h_expand * v_expand;
  numpix2 = numpix / 2;
  /* Expand input data enough to let all the output samples be generated
     by the standard loop.  Special-casing padded output would be more
     efficient.
  */
  expand_right_edge( input_data, cinfo->max_v_samp_factor,
                     cinfo->image_width, output_cols * h_expand );
  inrow = 0;
  for( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) {
    outptr = output_data[outrow];
    for( outcol = 0, outcol_h = 0; outcol < output_cols;
         outcol++, outcol_h += h_expand ) {
      outvalue = 0;
      for( v = 0; v < v_expand; v++ ) {
        inptr = input_data[inrow + v] + outcol_h;
        for( h = 0; h < h_expand; h++ ) {
          outvalue += ( JPEG_INT32 ) GETJSAMPLE( *inptr++ );
        }
      }
      *outptr++ = ( JSAMPLE )( ( outvalue + numpix2 ) / numpix );
    }
    inrow += v_expand;
  }
}


/*
   Downsample pixel values of a single component.
   This version handles the special case of a full-size component,
   without smoothing.
*/

METHODDEF( void )
fullsize_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr,
                     JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  /* Copy the data */
  jcopy_sample_rows( input_data, 0, output_data, 0,
                     cinfo->max_v_samp_factor, cinfo->image_width );
  /* Edge-expand */
  expand_right_edge( output_data, cinfo->max_v_samp_factor,
                     cinfo->image_width, compptr->width_in_blocks * DCTSIZE );
}


/*
   Downsample pixel values of a single component.
   This version handles the common case of 2:1 horizontal and 1:1 vertical,
   without smoothing.

   A note about the "bias" calculations: when rounding fractional values to
   integer, we do not want to always round 0.5 up to the next integer.
   If we did that, we'd introduce a noticeable bias towards larger values.
   Instead, this code is arranged so that 0.5 will be rounded up or down at
   alternate pixel locations (a simple ordered dither pattern).
*/

METHODDEF( void )
h2v1_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr,
                 JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  int outrow;
  JDIMENSION outcol;
  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
  register JSAMPROW inptr, outptr;
  register int bias;
  /* Expand input data enough to let all the output samples be generated
     by the standard loop.  Special-casing padded output would be more
     efficient.
  */
  expand_right_edge( input_data, cinfo->max_v_samp_factor,
                     cinfo->image_width, output_cols * 2 );
  for( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) {
    outptr = output_data[outrow];
    inptr = input_data[outrow];
    bias = 0;			/* bias = 0,1,0,1,... for successive samples */
    for( outcol = 0; outcol < output_cols; outcol++ ) {
      *outptr++ = ( JSAMPLE )( ( GETJSAMPLE( *inptr ) + GETJSAMPLE( inptr[1] )
                                 + bias ) >> 1 );
      bias ^= 1;		/* 0=>1, 1=>0 */
      inptr += 2;
    }
  }
}


/*
   Downsample pixel values of a single component.
   This version handles the standard case of 2:1 horizontal and 2:1 vertical,
   without smoothing.
*/

METHODDEF( void )
h2v2_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr,
                 JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  int inrow, outrow;
  JDIMENSION outcol;
  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
  register JSAMPROW inptr0, inptr1, outptr;
  register int bias;
  /* Expand input data enough to let all the output samples be generated
     by the standard loop.  Special-casing padded output would be more
     efficient.
  */
  expand_right_edge( input_data, cinfo->max_v_samp_factor,
                     cinfo->image_width, output_cols * 2 );
  inrow = 0;
  for( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) {
    outptr = output_data[outrow];
    inptr0 = input_data[inrow];
    inptr1 = input_data[inrow + 1];
    bias = 1;			/* bias = 1,2,1,2,... for successive samples */
    for( outcol = 0; outcol < output_cols; outcol++ ) {
      *outptr++ = ( JSAMPLE )( ( GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) +
                                 GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] )
                                 + bias ) >> 2 );
      bias ^= 3;		/* 1=>2, 2=>1 */
      inptr0 += 2;
      inptr1 += 2;
    }
    inrow += 2;
  }
}


#ifdef INPUT_SMOOTHING_SUPPORTED

/*
   Downsample pixel values of a single component.
   This version handles the standard case of 2:1 horizontal and 2:1 vertical,
   with smoothing.  One row of context is required.
*/

METHODDEF( void )
h2v2_smooth_downsample( j_compress_ptr cinfo, jpeg_component_info * compptr,
                        JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  int inrow, outrow;
  JDIMENSION colctr;
  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
  register JSAMPROW inptr0, inptr1, above_ptr, below_ptr, outptr;
  JPEG_INT32 membersum, neighsum, memberscale, neighscale;
  /* Expand input data enough to let all the output samples be generated
     by the standard loop.  Special-casing padded output would be more
     efficient.
  */
  expand_right_edge( input_data - 1, cinfo->max_v_samp_factor + 2,
                     cinfo->image_width, output_cols * 2 );
  /* We don't bother to form the individual "smoothed" input pixel values;
     we can directly compute the output which is the average of the four
     smoothed values.  Each of the four member pixels contributes a fraction
     (1-8*SF) to its own smoothed image and a fraction SF to each of the three
     other smoothed pixels, therefore a total fraction (1-5*SF)/4 to the final
     output.  The four corner-adjacent neighbor pixels contribute a fraction
     SF to just one smoothed pixel, or SF/4 to the final output; while the
     eight edge-adjacent neighbors contribute SF to each of two smoothed
     pixels, or SF/2 overall.  In order to use integer arithmetic, these
     factors are scaled by 2^16 = 65536.
     Also recall that SF = smoothing_factor / 1024.
  */
  memberscale = 16384 - cinfo->smoothing_factor * 80; /* scaled (1-5*SF)/4 */
  neighscale = cinfo->smoothing_factor * 16; /* scaled SF/4 */
  inrow = 0;
  for( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) {
    outptr = output_data[outrow];
    inptr0 = input_data[inrow];
    inptr1 = input_data[inrow + 1];
    above_ptr = input_data[inrow - 1];
    below_ptr = input_data[inrow + 2];
    /* Special case for first column: pretend column -1 is same as column 0 */
    membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) +
                GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] );
    neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) +
               GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) +
               GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[2] ) +
               GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[2] );
    neighsum += neighsum;
    neighsum += GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[2] ) +
                GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[2] );
    membersum = membersum * memberscale + neighsum * neighscale;
    *outptr++ = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
    inptr0 += 2;
    inptr1 += 2;
    above_ptr += 2;
    below_ptr += 2;
    for( colctr = output_cols - 2; colctr > 0; colctr-- ) {
      /* sum of pixels directly mapped to this output element */
      membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) +
                  GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] );
      /* sum of edge-neighbor pixels */
      neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) +
                 GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) +
                 GETJSAMPLE( inptr0[-1] ) + GETJSAMPLE( inptr0[2] ) +
                 GETJSAMPLE( inptr1[-1] ) + GETJSAMPLE( inptr1[2] );
      /* The edge-neighbors count twice as much as corner-neighbors */
      neighsum += neighsum;
      /* Add in the corner-neighbors */
      neighsum += GETJSAMPLE( above_ptr[-1] ) + GETJSAMPLE( above_ptr[2] ) +
                  GETJSAMPLE( below_ptr[-1] ) + GETJSAMPLE( below_ptr[2] );
      /* form final output scaled up by 2^16 */
      membersum = membersum * memberscale + neighsum * neighscale;
      /* round, descale and output it */
      *outptr++ = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
      inptr0 += 2;
      inptr1 += 2;
      above_ptr += 2;
      below_ptr += 2;
    }
    /* Special case for last column */
    membersum = GETJSAMPLE( *inptr0 ) + GETJSAMPLE( inptr0[1] ) +
                GETJSAMPLE( *inptr1 ) + GETJSAMPLE( inptr1[1] );
    neighsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( above_ptr[1] ) +
               GETJSAMPLE( *below_ptr ) + GETJSAMPLE( below_ptr[1] ) +
               GETJSAMPLE( inptr0[-1] ) + GETJSAMPLE( inptr0[1] ) +
               GETJSAMPLE( inptr1[-1] ) + GETJSAMPLE( inptr1[1] );
    neighsum += neighsum;
    neighsum += GETJSAMPLE( above_ptr[-1] ) + GETJSAMPLE( above_ptr[1] ) +
                GETJSAMPLE( below_ptr[-1] ) + GETJSAMPLE( below_ptr[1] );
    membersum = membersum * memberscale + neighsum * neighscale;
    *outptr = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
    inrow += 2;
  }
}


/*
   Downsample pixel values of a single component.
   This version handles the special case of a full-size component,
   with smoothing.  One row of context is required.
*/

METHODDEF( void )
fullsize_smooth_downsample( j_compress_ptr cinfo, jpeg_component_info *compptr,
                            JSAMPARRAY input_data, JSAMPARRAY output_data ) {
  int outrow;
  JDIMENSION colctr;
  JDIMENSION output_cols = compptr->width_in_blocks * DCTSIZE;
  register JSAMPROW inptr, above_ptr, below_ptr, outptr;
  JPEG_INT32 membersum, neighsum, memberscale, neighscale;
  int colsum, lastcolsum, nextcolsum;
  /* Expand input data enough to let all the output samples be generated
     by the standard loop.  Special-casing padded output would be more
     efficient.
  */
  expand_right_edge( input_data - 1, cinfo->max_v_samp_factor + 2,
                     cinfo->image_width, output_cols );
  /* Each of the eight neighbor pixels contributes a fraction SF to the
     smoothed pixel, while the main pixel contributes (1-8*SF).  In order
     to use integer arithmetic, these factors are multiplied by 2^16 = 65536.
     Also recall that SF = smoothing_factor / 1024.
  */
  memberscale = 65536L - cinfo->smoothing_factor * 512L; /* scaled 1-8*SF */
  neighscale = cinfo->smoothing_factor * 64; /* scaled SF */
  for( outrow = 0; outrow < compptr->v_samp_factor; outrow++ ) {
    outptr = output_data[outrow];
    inptr = input_data[outrow];
    above_ptr = input_data[outrow - 1];
    below_ptr = input_data[outrow + 1];
    /* Special case for first column */
    colsum = GETJSAMPLE( *above_ptr++ ) + GETJSAMPLE( *below_ptr++ ) +
             GETJSAMPLE( *inptr );
    membersum = GETJSAMPLE( *inptr++ );
    nextcolsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( *below_ptr ) +
                 GETJSAMPLE( *inptr );
    neighsum = colsum + ( colsum - membersum ) + nextcolsum;
    membersum = membersum * memberscale + neighsum * neighscale;
    *outptr++ = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
    lastcolsum = colsum;
    colsum = nextcolsum;
    for( colctr = output_cols - 2; colctr > 0; colctr-- ) {
      membersum = GETJSAMPLE( *inptr++ );
      above_ptr++;
      below_ptr++;
      nextcolsum = GETJSAMPLE( *above_ptr ) + GETJSAMPLE( *below_ptr ) +
                   GETJSAMPLE( *inptr );
      neighsum = lastcolsum + ( colsum - membersum ) + nextcolsum;
      membersum = membersum * memberscale + neighsum * neighscale;
      *outptr++ = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
      lastcolsum = colsum;
      colsum = nextcolsum;
    }
    /* Special case for last column */
    membersum = GETJSAMPLE( *inptr );
    neighsum = lastcolsum + ( colsum - membersum ) + colsum;
    membersum = membersum * memberscale + neighsum * neighscale;
    *outptr = ( JSAMPLE )( ( membersum + 32768 ) >> 16 );
  }
}

#endif /* INPUT_SMOOTHING_SUPPORTED */


void jinit_downsampler( j_compress_ptr cinfo ) {
  my_downsample_ptr downsample;
  int ci;
  jpeg_component_info * compptr;
  wxjpeg_boolean smoothok = TRUE;
  downsample = ( my_downsample_ptr )
               ( *cinfo->mem->alloc_small )( ( j_common_ptr ) cinfo, JPOOL_IMAGE,
                   SIZEOF( my_downsampler ) );
  cinfo->downsample = ( struct jpeg_downsampler * ) downsample;
  downsample->pub.start_pass = start_pass_downsample;
  downsample->pub.downsample = sep_downsample;
  downsample->pub.need_context_rows = FALSE;
  if( cinfo->CCIR601_sampling )
  { ERREXIT( cinfo, JERR_CCIR601_NOTIMPL ); }
  /* Verify we can handle the sampling factors, and set up method pointers */
  for( ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components;
       ci++, compptr++ ) {
    if( compptr->h_samp_factor == cinfo->max_h_samp_factor &&
        compptr->v_samp_factor == cinfo->max_v_samp_factor ) {
      #ifdef INPUT_SMOOTHING_SUPPORTED
      if( cinfo->smoothing_factor ) {
        downsample->methods[ci] = fullsize_smooth_downsample;
        downsample->pub.need_context_rows = TRUE;
      } else
      #endif
        downsample->methods[ci] = fullsize_downsample;
    } else if( compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
               compptr->v_samp_factor == cinfo->max_v_samp_factor ) {
      smoothok = FALSE;
      downsample->methods[ci] = h2v1_downsample;
    } else if( compptr->h_samp_factor * 2 == cinfo->max_h_samp_factor &&
               compptr->v_samp_factor * 2 == cinfo->max_v_samp_factor ) {
      #ifdef INPUT_SMOOTHING_SUPPORTED
      if( cinfo->smoothing_factor ) {
        downsample->methods[ci] = h2v2_smooth_downsample;
        downsample->pub.need_context_rows = TRUE;
      } else
      #endif
        downsample->methods[ci] = h2v2_downsample;
    } else if( ( cinfo->max_h_samp_factor % compptr->h_samp_factor ) == 0 &&
               ( cinfo->max_v_samp_factor % compptr->v_samp_factor ) == 0 ) {
      smoothok = FALSE;
      downsample->methods[ci] = int_downsample;
    } else
    { ERREXIT( cinfo, JERR_FRACT_SAMPLE_NOTIMPL ); }
  }
  #ifdef INPUT_SMOOTHING_SUPPORTED
  if( cinfo->smoothing_factor && !smoothok )
  { TRACEMS( cinfo, 0, JTRC_SMOOTH_NOTIMPL ); }
  #endif
}
